import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Timer;
import java.util.TimerTask;

public class NetworkSwitch {

    // 定义以太网1的常量名称，用于网络接口识别
    private static final String ETHERNET1 = "以太网 1";
    // 定义以太网2的常量名称，用于网络接口识别
    private static final String ETHERNET2 = "以太网 2";
    // 定义速度阈值为100KB/s，用于判断网络速度是否达到高速
    private static final long THRESHOLD_SPEED = 100 * 1024;
    // 定义持续高速状态的时长为5秒，用于判断网络是否长时间处于高速状态
    private static final int SUSTAINED_HIGH_SPEED_DURATION = 5000;
    // 定义持续低速状态的时长为200秒，用于判断网络是否长时间处于低速状态
    private static final int SUSTAINED_LOW_SPEED_DURATION = 200000;

    // 记录上一次接收的字节数，用于计算网络速度
    private static long lastBytesReceived = 0;
    // 记录上一次发送的字节数，用于计算网络速度
    private static long lastBytesSent = 0;
    // 记录高速状态开始的时间戳，用于计算持续高速状态的时长
    private static long highSpeedStartTime = 0;
    // 记录低速状态开始的时间戳，用于计算持续低速状态的时长
    private static long lowSpeedStartTime = 0;


    /**
     * 程序入口主方法。
     * 主要功能是监控网络速度，根据速度变化来切换网络适配器。
     * 使用定时任务每秒检查一次网络速度。
     *
     * @param args 命令行参数，未使用。
     */
    public static void main(String[] args) {
        // 启用第二个以太网适配器
        enableNetworkAdapter(ETHERNET2);
        // 禁用第一个以太网适配器
        disableNetworkAdapter(ETHERNET1);

        // 创建一个定时器，用于定期执行任务
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                // 获取第二个以太网适配器的当前速度
                long currentSpeed = getNetworkSpeed(ETHERNET2);

                // 根据当前速度判断是否超过阈值
                if (currentSpeed > THRESHOLD_SPEED) {
                    // 如果当前是首次检测到高速，记录开始时间
                    if (highSpeedStartTime == 0) {
                        highSpeedStartTime = System.currentTimeMillis();
                    } else if (System.currentTimeMillis() - highSpeedStartTime >= SUSTAINED_HIGH_SPEED_DURATION) {
                        // 如果高速状态持续时间超过设定值，重置低速开始时间
                        lowSpeedStartTime = 0;
                    }
                } else {
                    // 如果速度低于阈值
                    if (highSpeedStartTime != 0 && System.currentTimeMillis() - highSpeedStartTime >= SUSTAINED_HIGH_SPEED_DURATION) {
                        // 如果之前有高速状态且持续时间超过设定值
                        if (lowSpeedStartTime == 0) {
                            // 如果低速状态开始时间未记录，记录当前时间
                            lowSpeedStartTime = System.currentTimeMillis();
                        } else if (System.currentTimeMillis() - lowSpeedStartTime >= SUSTAINED_LOW_SPEED_DURATION) {
                            // 如果低速状态持续时间超过设定值，切换网络适配器
                            switchNetworkAdapters();
                            // 重置高速和低速开始时间
                            highSpeedStartTime = 0;
                            lowSpeedStartTime = 0;
                        }
                    } else {
                        // 如果未达到高速持续时间，重置高速开始时间
                        highSpeedStartTime = 0;
                    }
                }
            }
        }, 0, 1000); // 每秒执行一次任务
    }


    /**
     * 获取指定网卡的网络速度。
     * 通过计算两次读取网卡收发字节数的差值来估算网络速度。
     *
     * @param adapterName 网卡名称，用于识别要监测速度的网卡。
     * @return 返回网卡的网络速度，单位为字节/秒。如果无法获取网卡信息或发生错误，则返回0。
     */
    private static long getNetworkSpeed(String adapterName) {
        try {
            // 根据网卡名称获取网卡对象
            NetworkInterface networkInterface = NetworkInterface.getByName(adapterName);
            // 如果网卡不存在，则返回0
            if (networkInterface == null) {
                return 0;
            }

            // 获取所有网卡接口列表
            Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
            // 遍历所有网卡接口
            while (networkInterfaces.hasMoreElements()) {
                NetworkInterface ni = networkInterfaces.nextElement();
                // 找到指定名称的网卡
                if (ni.getName().equals(adapterName)) {
                    // 计算已接收的字节数
                    long bytesReceived = ni.getInterfaceAddresses().stream()
                            .mapToLong(addr -> {
                                try {
                                    // 这里的硬件地址长度似乎与接收字节数无关，可能是一个错误的计算
                                    return ni.getHardwareAddress().length;
                                } catch (SocketException e) {
                                    throw new RuntimeException(e);
                                }
                            })
                            .sum();
                    // 计算已发送的字节数
                    long bytesSent = ni.getInterfaceAddresses().stream()
                            .mapToLong(addr -> {
                                try {
                                    // MTU是最大传输单元，与发送字节数也似乎没有直接关系，可能是一个错误的计算
                                    return ni.getMTU();
                                } catch (SocketException e) {
                                    throw new RuntimeException(e);
                                }
                            })
                            .sum();

                    // 计算接收和发送速度
                    long receivedSpeed = bytesReceived - lastBytesReceived;
                    long sentSpeed = bytesSent - lastBytesSent;

                    // 更新上一次的收发字节数
                    lastBytesReceived = bytesReceived;
                    lastBytesSent = bytesSent;

                    // 返回接收和发送速度中的较大值作为网络速度
                    return Math.max(receivedSpeed, sentSpeed);
                }
            }
        } catch (SocketException e) {
            // 打印异常堆栈跟踪
            e.printStackTrace();
        }
        // 如果发生异常或无法找到指定的网卡，则返回0
        return 0;
    }


    /**
     * 启用指定的网络适配器。
     * <p>
     * 通过执行命令行指令来启用指定名称的网络适配器。此功能对于在程序中动态管理网络连接特别有用，
     * 比如在需要恢复网络连接或者切换网络环境时。
     *
     * @param adapterName 网络适配器的名称。这个名称是Windows网络适配器的唯一标识符，
     *                    通过这个标识符可以精确地控制对应的网络适配器。
     *                    调用者需要确保提供的适配器名称是准确且存在的。
     */
    private static void enableNetworkAdapter(String adapterName) {
        // 构造启用网络适配器的命令，并执行该命令。
        executeCommand("netsh interface set interface \"" + adapterName + "\" enable");
    }

    /**
     * 禁用网络适配器。
     * <p>
     * 通过执行命令行指令来禁用指定名称的网络适配器。
     * 这对于需要临时断开网络连接或管理网络适配器状态的场景非常有用。
     *
     * @param adapterName 网络适配器的名称。这是要禁用的具体网络适配器的标识符。
     *                    必须是正确识别网络适配器的唯一名称。
     */
    private static void disableNetworkAdapter(String adapterName) {
        // 构造命令行指令，使用netsh工具禁用指定的网络适配器。
        executeCommand("netsh interface set interface \"" + adapterName + "\" disable");
    }

    /**
     * 切换网络适配器。
     * 本函数的目的是在两个网络适配器之间进行切换，首先禁用一个，然后启用另一个。
     * 这种切换可能在需要重新配置网络连接或者解决网络问题时使用。
     *
     * @see disableNetworkAdapter 用于禁用网络适配器的函数
     * @see enableNetworkAdapter 用于启用网络适配器的函数
     */
    private static void switchNetworkAdapters() {
        disableNetworkAdapter(ETHERNET2);
        enableNetworkAdapter(ETHERNET1);
    }


    /**
     * 执行系统命令。
     * <p>
     * 通过ProcessBuilder创建一个进程来执行指定的系统命令，然后读取并打印命令的输出。
     * 此方法用于执行那些需要在操作系统级别完成的任务，例如操作文件系统或调用系统工具。
     *
     * @param command 要执行的系统命令字符串。
     */
    private static void executeCommand(String command) {
        try {
            // 创建ProcessBuilder实例，用于启动新进程并执行命令。
            ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", command);
            // 启动进程并获取进程实例。
            Process process = pb.start();

            // 通过进程的输入流读取命令的输出。
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String line;
            // 遍历命令输出的每一行并打印。
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }

            // 等待进程执行完毕。
            process.waitFor();
        } catch (Exception e) {
            // 打印异常堆栈跟踪，以便于问题诊断。
            e.printStackTrace();
        }
    }

}
